﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NcfDB.Exception;

namespace NcfDB.Model
{
    class Table
    {
        public string Name { get; set; }
        public List<string> fieldOrder;
        protected List<Row> rows;
        protected Dictionary<string, FieldProperty> fieldProperty;
        public static double maxBytesLength = 1000000;
        public List<Row> Rows { get { return rows; } }
        public Dictionary<string, FieldProperty>.ValueCollection FieldProperty { get { return this.fieldProperty.Values; } }
        public Row this[int index]
        {
            get
            {
                return this.Rows[index];
            }
        }
        public Table()
        {
            this.fieldOrder = new List<string>();
            this.fieldProperty = new Dictionary<string, FieldProperty>();
            this.rows = new List<Row>();
        }
        public FieldProperty getFieldProperty(string name)
        {
            if (this.fieldProperty.ContainsKey(name))
                return this.fieldProperty[name];
            else
                throw new DBAccessException("无效的字段");
        }
        public void setFieldProperty(string name,FieldProperty fp)
        {
            if (this.fieldProperty.ContainsKey(name))
                this.fieldProperty[name] = fp;
        }
        public void addFieldProperty(FieldProperty fp)
        {
            if(this.fieldProperty == null)
            {
                this.fieldOrder = new List<string>();
                this.fieldProperty = new Dictionary<string, FieldProperty>();
            }
            if (!this.fieldProperty.ContainsKey(fp.Name))
            {
                this.fieldProperty.Add(fp.Name, fp);
                this.fieldOrder.Add(fp.Name);
            }  
        }
        
        public void addRow(Row row)
        {
            if(this.rows == null)
            {
                this.rows = new List<Row>();
            }
            foreach (string key in row.Columns)
            {
                if (row[key] != null && row[key].ToString() != "")
                {
                    if (Utility.fieldTypeCheck(row[key].ToString(), this.getFieldProperty(key).Type) && row[key].ToString().Length <= this.getFieldProperty(key).Length)
                    {
                        if (this.getFieldProperty(key).isPrimaryKey == true)
                        {
                            foreach (Row r in this.rows)
                            {
                                if (row[key].ToString() == r[key].ToString())
                                    throw new DBAccessException("字段" + key + "不符合主键唯一性！");
                            }
                        }
                    }
                    else
                        throw new DBAccessException("字段" + key + "的类型不符或超过规定长度！");
                }
                else
                {
                    if (this.getFieldProperty(key).isPrimaryKey == false && this.getFieldProperty(key).acceptNull == true) ;
                    else
                        throw new DBAccessException("字段" + key + "不能为NULL！");
                }
            }
            this.rows.Add(row);
        }
        public void deleteRow(Row row)
        {
            rows.Remove(row);
        }
        public static Table readFromStream(ref FileStream dbfFs,ref FileStream datFs,ref StreamReader dbfSr,ref StreamReader datSr)
        {
            Table table = new Table();
            table.fieldOrder = new List<string>();
            table.rows = new List<Row>();
            //StreamReader sr = new StreamReader(fs);
            string line;
            table.fieldProperty = new Dictionary<string, FieldProperty>();
            line = dbfSr.ReadLine();
            if (line == "~")
                table.Name = dbfSr.ReadLine();
            else if (line == null)
                return null;
            else
                table.Name = line;
            FieldProperty fp = new FieldProperty();
            fp = Model.FieldProperty.readFromStream(ref dbfFs,ref dbfSr);
            while(fp != null)
            {
                table.fieldProperty.Add(fp.Name, fp);
                table.fieldOrder.Add(fp.Name);
                fp = Model.FieldProperty.readFromStream(ref dbfFs, ref dbfSr);
            }
            readRow(table, ref datFs,ref datSr);
            //directory.index = Utility.readIntFromStream(ref fs);
            //directory.fileCount = Utility.readIntFromStream(ref fs);
            //int[] InodeIndexBuffer = new int[directory.fileCount];
            //for (int i = 0; i < directory.fileCount; i++) InodeIndexBuffer[i] = Utility.readIntFromStream(ref fs);
            //directory.fileInodeIndex = InodeIndexBuffer;
            return table;
        }

        public static void readRow(Table table,ref FileStream fs,ref StreamReader sr)
        {
            string line;
            //StreamReader sr = new StreamReader(fs);
            line = sr.ReadLine();
            if (line == "~")
                table.Name = sr.ReadLine();
            else if (line == null)
                return;
            else
                table.Name = line;
            Row row = new Row();
            row = Model.Row.readFromStream(table, ref fs, ref sr);
            while(row != null)
            {
                table.rows.Add(row);
                row = Model.Row.readFromStream(table, ref fs, ref sr);
            }
        }

        public void saveField(ref FileStream fs)
        {
            int count = this.fieldOrder.Count;
            string key;
            StreamWriter sw = new StreamWriter(fs);
            sw.WriteLine("~");
            sw.WriteLine(this.Name);
            sw.Flush();
            for(int i = 0; i < count; i++)
            {
                key = this.fieldOrder[i];
                this.getFieldProperty(key).save(ref fs);
            }
        }
        public void saveRow(ref FileStream fs)
        {
            int count = this.rows.Count;
            StreamWriter sw = new StreamWriter(fs);
            sw.WriteLine("~");
            sw.WriteLine(this.Name);
            sw.Flush();
            for (int i = 0; i < count; i++)
            {
                rows[i].save(this,ref fs);
            }
            //Utility.writeBytes(Encoding.Default.GetBytes(this.Name), ref fs);
            //Utility.writeBytes(BitConverter.GetBytes(this.fileCount), ref fs);
            //foreach (FieldProperty fp in this.fieldProperty) Utility.writeBytesToStream(BitConverter.GetBytes(InodeIndex), ref fs);
        }
    }
}
